home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / wais / waisgate / HTFTP.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  26KB  |  915 lines

  1. /*            File Transfer Protocol (FTP) Client
  2. **            for a WorldWideWeb browser
  3. **            ===================================
  4. **
  5. **    A cache of control connections is kept.
  6. **
  7. ** Note: Port allocation
  8. **
  9. **    It is essential that the port is allocated by the system, rather
  10. **    than chosen in rotation by us (POLL_PORTS), or the following
  11. **    problem occurs.
  12. **
  13. **    It seems that an attempt by the server to connect to a port which has
  14. **    been used recently by a listen on the same socket, or by another
  15. **    socket this or another process causes a hangup of (almost exactly)
  16. **    one minute. Therefore, we have to use a rotating port number.
  17. **    The problem remains that if the application is run twice in quick
  18. **    succession, it will hang for what remains of a minute.
  19. **
  20. ** Authors
  21. **    TBL    Tim Berners-lee <timbl@info.cern.ch>
  22. **    DD    Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
  23. ** History:
  24. **     2 May 91    Written TBL, as a part of the WorldWideWeb project.
  25. **    15 Jan 92    Bug fix: close() was used for NETCLOSE for control soc
  26. **    10 Feb 92    Retry if cached connection times out or breaks
  27. **     8 Dec 92    Bug fix 921208 TBL after DD
  28. **    17 Dec 92    Anon FTP password now just WWWuser@ suggested by DD
  29. **            fails on princeton.edu!
  30. **
  31. ** Options:
  32. **    LISTEN        We listen, the other guy connects for data.
  33. **            Otherwise, other way round, but problem finding our
  34. **            internet address!
  35. **
  36. ** Bugs:
  37. **    No binary mode! Always uses ASCII! 
  38. */
  39.  
  40. #define LISTEN        /* @@@@ Test */
  41.  
  42. /*
  43. BUGS:    @@@      Limit connection cache size!
  44.         Error reporting to user.
  45.         400 & 500 errors are acked by user with windows.
  46.         Use configuration file for user names
  47.         Prompt user for password
  48.         
  49. **        Note for portablility this version does not use select() and
  50. **        so does not watch the control and data channels at the
  51. **        same time.
  52. */        
  53.  
  54. #include "HTFTP.h"    /* Implemented here */
  55.  
  56. #define CR   FROMASCII('\015')    /* Must be converted to ^M for transmission */
  57. #define LF   FROMASCII('\012')    /* Must be converted to ^J for transmission */
  58.  
  59. #define REPEAT_PORT    /* Give the port number for each file */
  60. #define REPEAT_LISTEN    /* Close each listen socket and open a new one */
  61.  
  62. /* define POLL_PORTS         If allocation does not work, poll ourselves.*/
  63. #define LISTEN_BACKLOG 2    /* Number of pending connect requests (TCP)*/
  64.  
  65. #define FIRST_TCP_PORT    1024    /* Region to try for a listening port */
  66. #define LAST_TCP_PORT    5999    
  67.  
  68. #define LINE_LENGTH 256
  69. #define COMMAND_LENGTH 256
  70.  
  71. #include "HTParse.h"
  72. #include "HTUtils.h"
  73. #include "tcp.h"
  74. #include "HTTCP.h"
  75. #include "HTAnchor.h"
  76. #include "HTFile.h"    /* For HTFileFormat() */
  77.  
  78. #ifndef IPPORT_FTP
  79. #define IPPORT_FTP    21
  80. #endif
  81.  
  82. #ifdef REMOVED_CODE
  83. extern char *malloc();
  84. extern void free();
  85. extern char *strncpy();
  86. #endif
  87.  
  88. typedef struct _connection {
  89.     struct _connection *    next;    /* Link on list     */
  90.     u_long            addr;    /* IP address        */
  91.     int                socket;    /* Socket number for communication */
  92.     BOOL            binary; /* Binary mode? */
  93. } connection;
  94.  
  95. #ifndef NIL
  96. #define NIL 0
  97. #endif
  98.  
  99. /*        Hypertext object building machinery
  100. */
  101. #include "HTML.h"
  102.  
  103. #define PUTC(c) (*targetClass.put_character)(target, c)
  104. #define PUTS(s) (*targetClass.put_string)(target, s)
  105. #define START(e) (*targetClass.start_element)(target, e, 0, 0)
  106. #define END(e) (*targetClass.end_element)(target, e)
  107. #define END_TARGET (*targetClass.end_document)(target)
  108. #define FREE_TARGET (*targetClass.free)(target)
  109. struct _HTStructured {
  110.     CONST HTStructuredClass *    isa;
  111.     /* ... */
  112. };
  113.  
  114.  
  115. /*    Module-Wide Variables
  116. **    ---------------------
  117. */
  118. PRIVATE connection * connections =0;    /* Linked list of connections */
  119. PRIVATE char    response_text[LINE_LENGTH+1];/* Last response from NewsHost */
  120. PRIVATE connection * control;        /* Current connection */
  121. PRIVATE int    data_soc = -1;        /* Socket for data transfer =invalid */
  122.  
  123. #ifdef POLL_PORTS
  124. PRIVATE    unsigned short    port_number = FIRST_TCP_PORT;
  125. #endif
  126.  
  127. #ifdef LISTEN
  128. PRIVATE int     master_socket = -1;    /* Listening socket = invalid    */
  129. PRIVATE char    port_command[255];    /* Command for setting the port */
  130. PRIVATE fd_set    open_sockets;         /* Mask of active channels */
  131. PRIVATE int    num_sockets;          /* Number of sockets to scan */
  132. #else
  133. PRIVATE    unsigned short    passive_port;    /* Port server specified for data */
  134. #endif
  135.  
  136.  
  137. #define NEXT_CHAR HTGetChararcter()    /* Use Function in HTFormat.c */
  138.  
  139. #define DATA_BUFFER_SIZE 2048
  140. PRIVATE char data_buffer[DATA_BUFFER_SIZE];        /* Input data buffer */
  141. PRIVATE char * data_read_pointer;
  142. PRIVATE char * data_write_pointer;
  143. #define NEXT_DATA_CHAR next_data_char()
  144.  
  145.  
  146. /*    Procedure: Read a character from the data connection
  147. **    ----------------------------------------------------
  148. */
  149. PRIVATE char next_data_char
  150. NOARGS
  151. {
  152.     int status;
  153.     if (data_read_pointer >= data_write_pointer) {
  154.     status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
  155.     /* Get some more data */
  156.     if (status <= 0) return (char)-1;
  157.     data_write_pointer = data_buffer + status;
  158.     data_read_pointer = data_buffer;
  159.     }
  160. #ifdef NOT_ASCII
  161.     {
  162.         char c = *data_read_pointer++;
  163.     return FROMASCII(c);
  164.     }
  165. #else
  166.     return *data_read_pointer++;
  167. #endif
  168. }
  169.  
  170.  
  171. /*    Close an individual connection
  172. **
  173. */
  174. #ifdef __STDC__
  175. PRIVATE int close_connection(connection * con)
  176. #else
  177. PRIVATE int close_connection(con)
  178.     connection *con;
  179. #endif
  180. {
  181.     connection * scan;
  182.     int status = NETCLOSE(con->socket);
  183.     if (TRACE) fprintf(stderr, "FTP: Closing control socket %d\n", con->socket);
  184.     if (connections==con) {
  185.         connections = con->next;
  186.     return status;
  187.     }
  188.     for(scan=connections; scan; scan=scan->next) {
  189.         if (scan->next == con) {
  190.         scan->next = con->next;    /* Unlink */
  191.         if (control==con) control = (connection*)0;
  192.         return status;
  193.     } /*if */
  194.     } /* for */
  195.     return -1;        /* very strange -- was not on list. */
  196. }
  197.  
  198.  
  199. /*    Execute Command and get Response
  200. **    --------------------------------
  201. **
  202. **    See the state machine illustrated in RFC959, p57. This implements
  203. **    one command/reply sequence.  It also interprets lines which are to
  204. **    be continued, which are marked with a "-" immediately after the
  205. **    status code.
  206. **
  207. ** On entry,
  208. **    con    points to the connection which is established.
  209. **    cmd    points to a command, or is NIL to just get the response.
  210. **
  211. **    The command is terminated with the CRLF pair.
  212. **
  213. ** On exit,
  214. **    returns:  The first digit of the reply type,
  215. **          or negative for communication failure.
  216. */
  217. #ifdef __STDC__
  218. PRIVATE int response(char * cmd)
  219. #else
  220. PRIVATE int response(cmd)
  221.     char * cmd;
  222. #endif
  223. {
  224.     int result;                /* Three-digit decimal code */
  225.     char    continuation;
  226.     int status;
  227.     
  228.     if (!control) {
  229.           if(TRACE) fprintf(stderr, "FTP: No control connection set up!!\n");
  230.       return -99;
  231.     }
  232.     
  233.     if (cmd) {
  234.     
  235.     if (TRACE) fprintf(stderr, "  Tx: %s", cmd);
  236.  
  237. #ifdef NOT_ASCII
  238.     {
  239.         char * p;
  240.         for(p=cmd; *p; p++) {
  241.             *p = TOASCII(*p);
  242.         }
  243.     }
  244. #endif 
  245.     status = NETWRITE(control->socket, cmd, (int)strlen(cmd));
  246.     if (status<0) {
  247.         if (TRACE) fprintf(stderr, 
  248.             "FTP: Error %d sending command: closing socket %d\n",
  249.         status, control->socket);
  250.         close_connection(control);
  251.         return status;
  252.     }
  253.     }
  254.  
  255.     do {
  256.     char *p = response_text;
  257.     for(;;) {  
  258.         if (((*p++=NEXT_CHAR) == LF)
  259.             || (p == &response_text[LINE_LENGTH])) {
  260.         *p++=0;            /* Terminate the string */
  261.         if (TRACE) fprintf(stderr, "    Rx: %s", response_text);
  262.         sscanf(response_text, "%d%c", &result, &continuation);
  263.         break;        
  264.         } /* if end of line */
  265.         
  266.         if (*(p-1) < 0) {
  267.         if(TRACE) fprintf(stderr, "Error on rx: closing socket %d\n",
  268.             control->socket);
  269.         strcpy(response_text, "000 *** TCP read error on response\n");
  270.             close_connection(control);
  271.             return -1;    /* End of file on response */
  272.         }
  273.     } /* Loop over characters */
  274.  
  275.     } while (continuation == '-');
  276.     
  277.     if (result==421) {
  278.     if(TRACE) fprintf(stderr, "FTP: They close so we close socket %d\n",
  279.         control->socket);
  280.     close_connection(control);
  281.     return -1;
  282.     }
  283.     return result/100;
  284. }
  285.  
  286.  
  287. /*    Get a valid connection to the host
  288. **    ----------------------------------
  289. **
  290. ** On entry,
  291. **    arg    points to the name of the host in a hypertext address
  292. ** On exit,
  293. **    returns    <0 if error
  294. **        socket number if success
  295. **
  296. **    This routine takes care of managing timed-out connections, and
  297. **    limiting the number of connections in use at any one time.
  298. **
  299. **    It ensures that all connections are logged in if they exist.
  300. **    It ensures they have the port number transferred.
  301. */
  302. PRIVATE int get_connection ARGS1 (CONST char *,arg)
  303. {
  304.     struct sockaddr_in soc_address;    /* Binary network address */
  305.     struct sockaddr_in* sin = &soc_address;
  306.  
  307.     char * username=0;
  308.     char * password=0;
  309.     
  310.     if (!arg) return -1;        /* Bad if no name sepcified    */
  311.     if (!*arg) return -1;        /* Bad if name had zero length    */
  312.  
  313. /*  Set up defaults:
  314. */
  315.     sin->sin_family = AF_INET;                /* Family, host order  */
  316.     sin->sin_port = htons(IPPORT_FTP);            /* Well Known Number    */
  317.  
  318.     if (TRACE) fprintf(stderr, "FTP: Looking for %s\n", arg);
  319.  
  320. /* Get node name:
  321. */
  322.     {
  323.     char *p1 = HTParse(arg, "", PARSE_HOST);
  324.     char *p2 = strrchr(p1, '@');    /* user? */
  325.     char * pw;
  326.     if (p2) {
  327.         username = p1;
  328.         *p2=0;            /* terminate */
  329.         p1 = p2+1;            /* point to host */
  330.         pw = strchr(username, ':');
  331.         if (pw) {
  332.             *pw++ = 0;
  333.         password = pw;
  334.         }
  335.     }
  336.     if (HTParseInet(sin, p1)) { free(p1); return -1;} /* TBL 920622 */
  337.  
  338.         if (!username) free(p1);
  339.     } /* scope of p1 */
  340.  
  341.         
  342. /*    Now we check whether we already have a connection to that port.
  343. */
  344.  
  345.     {
  346.     connection * scan;
  347.     for (scan=connections; scan; scan=scan->next) {
  348.         if (sin->sin_addr.s_addr == scan->addr) {
  349.           if (TRACE) fprintf(stderr, 
  350.         "FTP: Already have connection for %d.%d.%d.%d.\n",
  351.             (int)*((unsigned char *)(&scan->addr)+0),
  352.             (int)*((unsigned char *)(&scan->addr)+1),
  353.             (int)*((unsigned char *)(&scan->addr)+2),
  354.             (int)*((unsigned char *)(&scan->addr)+3));
  355.         if (username) free(username);
  356.         return scan->socket;        /* Good return */
  357.         } else {
  358.           if (TRACE) fprintf(stderr, 
  359.         "FTP: Existing connection is %d.%d.%d.%d\n",
  360.             (int)*((unsigned char *)(&scan->addr)+0),
  361.             (int)*((unsigned char *)(&scan->addr)+1),
  362.             (int)*((unsigned char *)(&scan->addr)+2),
  363.             (int)*((unsigned char *)(&scan->addr)+3));
  364.         }
  365.     }
  366.     }
  367.  
  368.    
  369. /*    Now, let's get a socket set up from the server:
  370. */      
  371.     {
  372.         int status;
  373.     connection * con = (connection *)malloc(sizeof(*con));
  374.     if (con == NULL) outofmem(__FILE__, "get_connection");
  375.     con->addr = sin->sin_addr.s_addr;    /* save it */
  376.     con->binary = NO;
  377.     status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  378.     if (status<0) {
  379.         (void) HTInetStatus("socket");
  380.         free(con);
  381.         if (username) free(username);
  382.         return status;
  383.     }
  384.     con->socket = status;
  385.  
  386.         status = connect(con->socket, (struct sockaddr*)&soc_address,
  387.      sizeof(soc_address));
  388.         if (status<0){
  389.         (void) HTInetStatus("connect");
  390.         if (TRACE) fprintf(stderr, 
  391.             "FTP: Unable to connect to remote host for `%s'.\n",
  392.             arg);
  393.         NETCLOSE(con->socket);
  394.         free(con);
  395.         if (username) free(username);
  396.         return status;            /* Bad return */
  397.     }
  398.     
  399.     if (TRACE) fprintf(stderr, "FTP connected, socket %d\n", con->socket);
  400.     control = con;            /* Current control connection */
  401.     con->next = connections;    /* Link onto list of good ones */
  402.     connections = con;
  403.         HTInitInput(con->socket);/* Initialise buffering for contron connection */
  404.  
  405.  
  406. /*    Now we log in        Look up username, prompt for pw.
  407. */
  408.     {
  409.         int status = response(NIL);    /* Get greeting */
  410.         
  411.         if (status == 2) {        /* Send username */
  412.             char * command;
  413.         if (username) {
  414.             command = (char*)malloc(10+strlen(username)+2+1);
  415.             if (command == NULL) outofmem(__FILE__, "get_connection");
  416.             sprintf(command, "USER %s%c%c", username, CR, LF);
  417.         } else {
  418.             command = (char*)malloc(25);
  419.             if (command == NULL) outofmem(__FILE__, "get_connection");
  420.             sprintf(command, "USER anonymous%c%c", CR, LF);
  421.             }
  422.         status = response(command);
  423.         free(command);
  424.         }
  425.         if (status == 3) {        /* Send password */
  426.             char * command;
  427.         if (password) {
  428.             command = (char*)malloc(10+strlen(password)+2+1);
  429.             if (command == NULL) outofmem(__FILE__, "get_connection");
  430.             sprintf(command, "PASS %s%c%c", password, CR, LF);
  431.         } else {
  432. #ifdef ANON_FTP_HOSTNAME
  433.             command = (char*)malloc(20+strlen(HTHostName())+2+1);
  434.             if (command == NULL) outofmem(__FILE__, "get_connection");
  435.             sprintf(command,
  436.             "PASS WWWuser@%s%c%c", HTHostName(), CR, LF); /*@@*/
  437. #else
  438.             /* In fact ftp.uu.net for example prefers just "@"
  439.                 the fulle domain name, which we can't get - DD */
  440.             command = (char*)malloc(20);
  441.             if (command == NULL) outofmem(__FILE__, "get_connection");
  442.             sprintf(command, "PASS WWWuser@%c%c", CR, LF); /*@@*/
  443. #endif
  444.             }
  445.         status = response(command);
  446.         free(command);
  447.         }
  448.             if (username) free(username);
  449.  
  450.         if (status == 3) {
  451.             char temp[80];
  452.         sprintf(temp, "ACCT noaccount%c%c", CR, LF);
  453.         status = response(temp);
  454.         }
  455.         if (status !=2) {
  456.             if (TRACE) fprintf(stderr, "FTP: Login fail: %s", response_text);
  457.             if (control) close_connection(control);
  458.             return -1;        /* Bad return */
  459.         }
  460.         if (TRACE) fprintf(stderr, "FTP: Logged in.\n");
  461.     }
  462.  
  463. /*    Now we inform the server of the port number we will listen on
  464. */
  465. #ifndef REPEAT_PORT
  466.     {
  467.         int status = response(port_command);
  468.         if (status !=2) {
  469.             if (control) close_connection(control);
  470.             return -status;        /* Bad return */
  471.         }
  472.         if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
  473.     }
  474. #endif
  475.     return con->socket;            /* Good return */
  476.     } /* Scope of con */
  477. }
  478.  
  479.  
  480. #ifdef LISTEN
  481.  
  482. /*    Close Master (listening) socket
  483. **    -------------------------------
  484. **
  485. **
  486. */
  487. #ifdef __STDC__
  488. PRIVATE int close_master_socket(void)
  489. #else
  490. PRIVATE int close_master_socket()
  491. #endif
  492. {
  493.     int status;
  494.     FD_CLR(master_socket, &open_sockets);
  495.     status = NETCLOSE(master_socket);
  496.     if (TRACE) fprintf(stderr, "FTP: Closed master socket %d\n", master_socket);
  497.     master_socket = -1;
  498.     if (status<0) return HTInetStatus("close master socket");
  499.     else return status;
  500. }
  501.  
  502.  
  503. /*    Open a master socket for listening on
  504. **    -------------------------------------
  505. **
  506. **    When data is transferred, we open a port, and wait for the server to
  507. **    connect with the data.
  508. **
  509. ** On entry,
  510. **    master_socket    Must be negative if not set up already.
  511. ** On exit,
  512. **    Returns        socket number if good
  513. **            less than zero if error.
  514. **    master_socket    is socket number if good, else negative.
  515. **    port_number    is valid if good.
  516. */
  517. #ifdef __STDC__
  518. PRIVATE int get_listen_socket(void)
  519. #else
  520. PRIVATE int get_listen_socket()
  521. #endif
  522. {
  523.     struct sockaddr_in soc_address;    /* Binary network address */
  524.     struct sockaddr_in* sin = &soc_address;
  525.     int new_socket;            /* Will be master_socket */
  526.     
  527.     
  528.     FD_ZERO(&open_sockets);    /* Clear our record of open sockets */
  529.     num_sockets = 0;
  530.     
  531. #ifndef REPEAT_LISTEN
  532.     if (master_socket>=0) return master_socket;  /* Done already */
  533. #endif
  534.  
  535. /*  Create internet socket
  536. */
  537.     new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  538.     
  539.     if (new_socket<0)
  540.     return HTInetStatus("socket for master socket");
  541.     
  542.     if (TRACE) fprintf(stderr, "FTP: Opened master socket number %d\n", new_socket);
  543.     
  544. /*  Search for a free port.
  545. */
  546.     sin->sin_family = AF_INET;        /* Family = internet, host order  */
  547.     sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */
  548. #ifdef POLL_PORTS
  549.     {
  550.         unsigned short old_port_number = port_number;
  551.     for(port_number=old_port_number+1;;port_number++){ 
  552.         int status;
  553.         if (port_number > LAST_TCP_PORT)
  554.         port_number = FIRST_TCP_PORT;
  555.         if (port_number == old_port_number) {
  556.         return HTInetStatus("bind");
  557.         }
  558.         soc_address.sin_port = htons(port_number);
  559.         if ((status=bind(new_socket,
  560.             (struct sockaddr*)&soc_address,
  561.                 /* Cast to generic sockaddr */
  562.             sizeof(soc_address))) == 0)
  563.         break;
  564.         if (TRACE) fprintf(stderr, 
  565.             "TCP bind attempt to port %d yields %d, errno=%d\n",
  566.         port_number, status, errno);
  567.     } /* for */
  568.     }
  569. #else
  570.     {
  571.         int status;
  572.     int address_length = sizeof(soc_address);
  573.     status = getsockname(control->socket,
  574.             (struct sockaddr *)&soc_address,
  575.              &address_length);
  576.     if (status<0) return HTInetStatus("getsockname");
  577.     CTRACE(tfp, "FTP: This host is %s\n",
  578.         HTInetString(sin));
  579.     
  580.     soc_address.sin_port = 0;    /* Unspecified: please allocate */
  581.     status=bind(new_socket,
  582.         (struct sockaddr*)&soc_address,
  583.             /* Cast to generic sockaddr */
  584.         sizeof(soc_address));
  585.     if (status<0) return HTInetStatus("bind");
  586.     
  587.     address_length = sizeof(soc_address);
  588.     status = getsockname(new_socket,
  589.             (struct sockaddr*)&soc_address,
  590.             &address_length);
  591.     if (status<0) return HTInetStatus("getsockname");
  592.     }
  593. #endif    
  594.  
  595.     CTRACE(tfp, "FTP: bound to port %d on %s\n",
  596.             (int)ntohs(sin->sin_port),
  597.         HTInetString(sin));
  598.  
  599. #ifdef REPEAT_LISTEN
  600.     if (master_socket>=0)
  601.         (void) close_master_socket();
  602. #endif    
  603.     
  604.     master_socket = new_socket;
  605.     
  606. /*    Now we must find out who we are to tell the other guy
  607. */
  608.     (void)HTHostName();     /* Make address valid - doesn't work*/
  609.     sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
  610.             (int)*((unsigned char *)(&sin->sin_addr)+0),
  611.             (int)*((unsigned char *)(&sin->sin_addr)+1),
  612.             (int)*((unsigned char *)(&sin->sin_addr)+2),
  613.             (int)*((unsigned char *)(&sin->sin_addr)+3),
  614.             (int)*((unsigned char *)(&sin->sin_port)+0),
  615.             (int)*((unsigned char *)(&sin->sin_port)+1),
  616.             CR, LF);
  617.  
  618.  
  619. /*    Inform TCP that we will accept connections
  620. */
  621.     if (listen(master_socket, 1)<0) {
  622.     master_socket = -1;
  623.     return HTInetStatus("listen");
  624.     }
  625.     CTRACE(tfp, "TCP: Master socket(), bind() and listen() all OK\n");
  626.     FD_SET(master_socket, &open_sockets);
  627.     if ((master_socket+1) > num_sockets) num_sockets=master_socket+1;
  628.  
  629.     return master_socket;        /* Good */
  630.  
  631. } /* get_listen_socket */
  632. #endif
  633.  
  634.  
  635.  
  636. /*    Read a directory into an hypertext object from the data socket
  637. **    --------------------------------------------------------------
  638. **
  639. ** On entry,
  640. **    anchor        Parent anchor to link the this node to
  641. **    address        Address of the directory
  642. ** On exit,
  643. **    returns        HT_LOADED if OK
  644. **            <0 if error.
  645. */
  646. PRIVATE int read_directory
  647. ARGS4 (
  648.   HTParentAnchor *,        parent,
  649.   CONST char *,            address,
  650.   HTFormat,            format_out,
  651.   HTStream *,            sink
  652. )
  653. {
  654.   HTStructured* target = HTML_new(parent, format_out, sink);
  655.   HTStructuredClass targetClass;
  656.   BOOL present[HTML_A_ATTRIBUTES];
  657.   char * value[HTML_A_ATTRIBUTES];
  658.   char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
  659.  
  660.   char c = 0;
  661. #define LASTPATH_LENGTH 150  /* @@@@ Horrible limit on the entry size */
  662.   char lastpath[LASTPATH_LENGTH + 1];
  663.   char *p, *entry;
  664.  
  665.   { int i; for(i=0; i<HTML_A_ATTRIBUTES; i++) present[i] = (i==HTML_A_HREF);}
  666.  
  667.   targetClass = *(target->isa);
  668.  
  669.   START(HTML_TITLE);
  670.   PUTS("FTP Directory of ");
  671.   PUTS(address + 5);  /* +5 gets rid of "file:" */
  672.   END(HTML_TITLE);
  673.   
  674.   START(HTML_H1);
  675.   PUTS(filename);
  676.   END(HTML_H1);
  677.   
  678.   present[HTML_A_HREF] = YES;        /* Attribute list for anchors */
  679.   data_read_pointer = data_write_pointer = data_buffer;
  680.  
  681.   if (*filename == 0)  /* Empty filename : use root */
  682.     strcpy (lastpath, "/");
  683.   else {
  684.     p = filename + strlen (filename) - 2;
  685.     while (p >= filename && *p != '/')
  686.       p--;
  687.     strcpy (lastpath, p + 1);  /* relative path */
  688.   }
  689.   free (filename);
  690.   entry = lastpath + strlen (lastpath);
  691.   if (*(entry-1) != '/')
  692.     *entry++ = '/';  /* ready to append entries */
  693.  
  694.   if (strlen (lastpath) > 1) {  /* Current file is not the FTP root */
  695.     strcpy (entry, "..");
  696.     value[HTML_A_HREF] = lastpath;
  697.     (*targetClass.start_element)(target, HTML_A, present,
  698.         (CONST char**) value);    /* typecast for think c */
  699.     PUTS("Up to Parent Directory");
  700.     END(HTML_A);
  701.   }
  702.  
  703.   START(HTML_DIR);
  704.   for (;;) {
  705.     p = entry;
  706.     while (p - lastpath < LASTPATH_LENGTH) {
  707.       c = NEXT_DATA_CHAR;
  708.       if (c == '\r') {
  709.     c = NEXT_DATA_CHAR;
  710.     if (c != LF) {
  711.       if (TRACE)
  712.         printf ("Warning: No newline but %d after carriage return.\n", c);
  713.       break;
  714.     }
  715.       }
  716.       if (c == LF || c == (char) EOF)
  717.     break;
  718.       *p++ = c;
  719.     }
  720.     if (c == (char) EOF && p == entry)
  721.       break;
  722.     *p = 0;
  723.     START(HTML_LI);
  724.     value[HTML_A_HREF] = lastpath;
  725.     (*targetClass.start_element)(target, HTML_A, present,
  726.         (CONST char**)value);    /* typecast for think c */
  727.     PUTS(entry);
  728.     END(HTML_A);
  729.   }
  730.  
  731.   END(HTML_DIR);
  732.   END_TARGET;
  733.   FREE_TARGET;
  734.  
  735.   return response(NIL) == 2 ? HT_LOADED : -1;
  736. }
  737.  
  738.  
  739. /*    Retrieve File from Server
  740. **    -------------------------
  741. **
  742. ** On entry,
  743. **    name        WWW address of a file: document, including hostname
  744. ** On exit,
  745. **    returns        Socket number for file if good.
  746. **            <0 if bad.
  747. */
  748. PUBLIC int HTFTPLoad
  749. ARGS4 (
  750.   CONST char *,            name,
  751.   HTParentAnchor *,        anchor,
  752.   HTFormat,            format_out,
  753.   HTStream *,            sink
  754. )
  755. {
  756.     BOOL isDirectory = NO;
  757.     int status;
  758.     int retry;            /* How many times tried? */
  759.     HTFormat format;
  760.     
  761.     for (retry=0; retry<2; retry++) {    /* For timed out/broken connections */
  762.     
  763.     status = get_connection(name);
  764.     if (status<0) return status;
  765.  
  766. #ifdef LISTEN
  767.     status = get_listen_socket();
  768.     if (status<0) return status;
  769.     
  770. #ifdef REPEAT_PORT
  771. /*    Inform the server of the port number we will listen on
  772. */
  773.     {
  774.         status = response(port_command);
  775.         if (status !=2) {        /* Could have timed out */
  776.         if (status<0) continue;        /* try again - net error*/
  777.         return -status;            /* bad reply */
  778.         }
  779.         if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
  780.     }
  781. #endif
  782. #else    /* Use PASV */
  783. /*    Tell the server to be passive
  784. */
  785.     {
  786.         char *p;
  787.         int reply, h0, h1, h2, h3, p0, p1;    /* Parts of reply */
  788.         status = response("PASV%c%c", CR, LF);
  789.         if (status !=2) {
  790.         if (status<0) continue;        /* retry or Bad return */
  791.         return -status;            /* bad reply */
  792.         }
  793.         for(p=response_text; *p; p++)
  794.         if ((*p<'0')||(*p>'9')) *p = ' ';    /* Keep only digits */
  795.         status = sscanf(response_text, "%d%d%d%d%d%d%d",
  796.             &reply, &h0, &h1, &h2, &h3, &p0, &p1);
  797.         if (status<5) {
  798.         if (TRACE) fprintf(stderr, "FTP: PASV reply has no inet address!\n");
  799.         return -99;
  800.         }
  801.         passive_port = (p0<<8) + p1;
  802.         if (TRACE) fprintf(stderr, "FTP: Server is listening on port %d\n",
  803.             passive_port);
  804.     }
  805.  
  806. /*    Open connection for data:
  807. */
  808.     {
  809.         struct sockaddr_in soc_address;
  810.         int status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  811.         if (status<0) {
  812.         (void) HTInetStatus("socket for data socket");
  813.         return status;
  814.         }
  815.         data_soc = status;
  816.         
  817.         soc_address.sin_addr.s_addr = control->addr;
  818.         soc_address.sin_port = htons(passive_port);
  819.         soc_address.sin_family = AF_INET;        /* Family, host order  */
  820.         if (TRACE) fprintf(stderr,  
  821.         "FTP: Data remote address is port %d, inet %d.%d.%d.%d\n",
  822.             (unsigned int)ntohs(soc_address.sin_port),
  823.             (int)*((unsigned char *)(&soc_address.sin_addr)+0),
  824.             (int)*((unsigned char *)(&soc_address.sin_addr)+1),
  825.             (int)*((unsigned char *)(&soc_address.sin_addr)+2),
  826.             (int)*((unsigned char *)(&soc_address.sin_addr)+3));
  827.     
  828.         status = connect(data_soc, (struct sockaddr*)&soc_address,
  829.             sizeof(soc_address));
  830.         if (status<0){
  831.         (void) HTInetStatus("connect for data");
  832.         NETCLOSE(data_soc);
  833.         return status;            /* Bad return */
  834.         }
  835.         
  836.         if (TRACE) fprintf(stderr, "FTP data connected, socket %d\n", data_soc);
  837.     }
  838. #endif /* use PASV */
  839.     status = 0;
  840.         break;    /* No more retries */
  841.  
  842.     } /* for retries */
  843.     if (status<0) return status;    /* Failed with this code */
  844.     
  845. /*    Ask for the file:
  846. */    
  847.     {
  848.         char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
  849.     char command[LINE_LENGTH+1];
  850.     if (!*filename) StrAllocCopy(filename, "/");
  851.     sprintf(command, "RETR %s%c%c", filename, CR, LF);
  852.     format = HTFileFormat(filename);
  853.     status = response(command);
  854.     if (status != 1) {  /* Failed : try to CWD to it */
  855.       sprintf(command, "CWD %s%c%c", filename, CR, LF);
  856.       status = response(command);
  857.       if (status == 2) {  /* Successed : let's NAME LIST it */
  858.         isDirectory = YES;
  859.         sprintf(command, "NLST%c%c", CR, LF);
  860.         status = response (command);
  861.       }
  862.     }
  863.     free(filename);
  864.     if (status != 1) return -status;        /* Action not started */
  865.     }
  866.  
  867. #ifdef LISTEN
  868. /*    Wait for the connection
  869. */
  870.     {
  871.     struct sockaddr_in soc_address;
  872.         int    soc_addrlen=sizeof(soc_address);
  873.     status = accept(master_socket,
  874.             (struct sockaddr *)&soc_address,
  875.             &soc_addrlen);
  876.     if (status<0)
  877.         return HTInetStatus("accept");
  878.     CTRACE(tfp, "TCP: Accepted new socket %d\n", status);
  879.     data_soc = status;
  880.     }
  881. #else
  882. /* @@ */
  883. #endif
  884.     if (isDirectory) {
  885.     return read_directory (anchor, name, format_out, sink);
  886.       /* returns HT_LOADED or error */
  887.     } else {
  888.     HTParseSocket(format, format_out,
  889.         anchor, data_soc, sink);
  890.         
  891.     HTInitInput(control->socket);
  892.     /* Reset buffering to control connection DD 921208 */
  893.     
  894.     status = NETCLOSE(data_soc);
  895.     if (TRACE) fprintf(stderr, "FTP: Closing data socket %d\n", data_soc);
  896.     if (status<0) (void) HTInetStatus("close");    /* Comment only */
  897.     data_soc = -1;    /* invalidate it */
  898.     
  899.     status = response(NIL);        /* Pick up final reply */
  900.     if (status!=2) return -status;
  901.  
  902.     return HT_LOADED;
  903.     }       
  904. } /* open_file_read */
  905.  
  906.  
  907.  
  908. /* this used to be a special case of HTFile, and the map was getting 
  909.  * in the way... hess
  910.  * this uses ftp for all ftp access !
  911.  * and does not try to be smart about local file accesses 
  912.  */
  913. PUBLIC HTProtocol HTFTP  = { "ftp", HTFTPLoad, 0 };
  914.  
  915.